{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1dc50180-1f70-4850-809f-cdd74e842dcc",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Gini Index / Entropy (Information Theory) / MSE Calculations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "44c1e63e-8b0f-41fe-a631-9c6f425933da",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5cfa1e21-d505-46eb-a431-ea33d72e740c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Gini / Entropy calculations =====================================================================\n",
    "def construct_dict_count(iter_items):\n",
    "    dict_counts = {}\n",
    "    for item in iter_items:\n",
    "        # get(key value, default value if not present)\n",
    "        dict_counts[item] = dict_counts.get(item, 0) + 1\n",
    "\n",
    "    return dict_counts\n",
    "\n",
    "def calculate_gini(list_elements):\n",
    "    \"\"\"\n",
    "    Gini = 1 - sum( count_i**2 / sum(count_i)**2 ) over all i\n",
    "    \"\"\"\n",
    "    if len(list_elements)==0:\n",
    "        return 1\n",
    "\n",
    "    iter_counts = construct_dict_count(list_elements).values()\n",
    "    n = sum(iter_counts)\n",
    "\n",
    "    array_terms = numpy.array([p_i**2 for p_i in iter_counts])\n",
    "    array_terms_normalized = numpy.divide(array_terms, n**2) # n can be 0\n",
    "    scalar_sum = numpy.sum(array_terms_normalized)\n",
    "\n",
    "    return 1 - scalar_sum\n",
    "\n",
    "def calculate_entropy(list_elements):\n",
    "    \"\"\"\n",
    "    Entropy from Information Theory:\n",
    "\n",
    "     - log_2( (1 /n) * v_counts ) \\cdot ( (1 /n) * v_counts )\n",
    "\n",
    "    \"\"\"\n",
    "    if len(list_elements)==0:\n",
    "        return 0\n",
    "\n",
    "    iter_counts = construct_dict_count(list_elements).values()\n",
    "    n = sum(iter_counts)\n",
    "\n",
    "    array_terms = numpy.array(list(iter_counts))\n",
    "    scalar = numpy.divide(1, n)\n",
    "    array_terms_scaled = numpy.multiply(scalar, array_terms)\n",
    "\n",
    "    return -numpy.dot(numpy.log2(array_terms_scaled), array_terms_scaled)\n",
    "\n",
    "def calculate_weighted_auxillary(list_elements, index_split, function):\n",
    "    \"\"\"\n",
    "    Weighted Auxillary = (1/num_elements) * ( function(LHS)*card(LHS) + function(RHS)*card(RHS) )\n",
    "\n",
    "    Function can return scalar or vector\n",
    "    \"\"\"\n",
    "    assert 0 <= index_split < len(list_elements)\n",
    "\n",
    "    LHS = list_elements[:index_split]\n",
    "    RHS = list_elements[index_split:]\n",
    "\n",
    "    scalar = numpy.divide(1, len(list_elements))\n",
    "    LHS_weighted = numpy.dot(function(LHS), len(LHS))\n",
    "    RHS_weighted = numpy.dot(function(RHS), len(RHS))\n",
    "\n",
    "    return scalar*(LHS_weighted + RHS_weighted)\n",
    "\n",
    "def calculate_weighted_something(list_elements, index_split, function):\n",
    "    \"\"\"\n",
    "    No idea = (1/num_elements) * ( function(LHS) \\cdot function(LHS) + function(RHS) \\cdot function(RHS) )\n",
    "\n",
    "    Function can return scalar or vector\n",
    "    \"\"\"\n",
    "    assert 0 <= index_split < len(list_elements)\n",
    "\n",
    "    LHS = list_elements[:index_split]\n",
    "    RHS = list_elements[index_split:]\n",
    "\n",
    "    scalar = numpy.divide(1, len(list_elements))\n",
    "    LHS_weighted = numpy.dot(function(LHS), function(LHS))\n",
    "    RHS_weighted = numpy.dot(function(RHS), function(RHS))\n",
    "\n",
    "    return scalar*(LHS_weighted + RHS_weighted)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d4c04244-9cf6-4d9e-a0dd-786cd33f11c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Mean Squared Error (MSE) Calculations ===========================================================\n",
    "def subtract_mean(array):\n",
    "    \"\"\"\n",
    "    Resulting set is has mean 0\n",
    "    \"\"\"\n",
    "    if not numpy.any(array): # empty\n",
    "        return 0\n",
    "    return array - numpy.mean(array)\n",
    "\n",
    "def calculate_sum_squares(list_elements):\n",
    "    \"\"\"\n",
    "    sum ( a_i**2 )\n",
    "    \"\"\"\n",
    "    array_terms = numpy.array(list_elements)\n",
    "    array_squares = numpy.square(array_terms)\n",
    "    scalar_sum = numpy.sum(array_squares)\n",
    "\n",
    "    return scalar_sum\n",
    "\n",
    "def calculate_mse(list_elements, index_split, function):\n",
    "    \"\"\"\n",
    "    MSE = (1/n) * ( sum( (x_i - f(x))**2 ) )\n",
    "    in which we split the sum between LHS and RHS\n",
    "    \"\"\"\n",
    "\n",
    "    scalar = numpy.divide(1, len(list_elements))\n",
    "    LHS = list_elements[:index_split]\n",
    "    RHS = list_elements[index_split:]\n",
    "\n",
    "    LHS_terms = numpy.subtract(LHS, function(LHS))\n",
    "    RHS_terms = numpy.subtract(RHS, function(RHS))\n",
    "\n",
    "    LHS_scalar = calculate_sum_squares(LHS_terms)\n",
    "    RHS_scalar = calculate_sum_squares(RHS_terms)\n",
    "\n",
    "    return scalar*( LHS_scalar + RHS_scalar)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17c84ce5-e537-4142-b0f8-a815f81058a0",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
